//stdstring.cpp
//#include "stdafx.h"
#include "object.h"
#include <fstream>
#include "base_time.h"
// Class StdString
using namespace std;

#ifdef _WIN32

	#include <mbstring.h>

#else // _WIN32

long _wtoi(const wchar_t *ptr)
{
	wchar_t *end_ptr = NULL;
	return wcstol(ptr, &end_ptr, 10);
}

#endif // _WIN32

HT_NAMESPACE_BEGIN

// #include <mbstring.h>

#define __imax(a,b) (((a) > (b)) ? (a) : (b))
#define __imin(a,b) (((a) < (b)) ? (a) : (b))

int __GetFormatLen(unsigned char * lpszFormat, va_list argList);

#define ANSI            /* Comment out for UNIX version     */
#ifdef ANSI             /* ANSI compatible version          */
#	include <stdarg.h>
int FormatString(HTSTD::string &str, char *lpszFormat, ...);
int FormatString(HTSTD::string &str, char *lpszFormat, ...)
{
	va_list argList;
	va_start(argList, lpszFormat);
	int Len = __GetFormatLen((unsigned char *)lpszFormat, argList);
	str.resize(Len);
	Len = vsprintf((char *)str.c_str(), lpszFormat, argList);
	va_end(argList);
	return Len;
}

#else                   /* UNIX compatible version          */
#	include <varargs.h>

int FormatString(sstring &str, char *lpszFormat);
int FormatString(sstring &str, char *lpszFormat)
va_dcl	
{
	va_list argList;
	va_start(argList);
	int Len = GetFormatLen((unsigned char *)lpszFormat, argList);
	str.resize(Len);
	Len = sprintf((char *)str.c_str(), argList);
	va_end(argList);
	return Len;
}

#endif

#if !defined ASSERT
#	include <assert.h>
#	define ASSERT			assert
#endif // ASSERT

#define FORCE_ANSI      0x10000
#define FORCE_UNICODE   0x20000
#define TCHAR_ARG		int
#define WCHAR_ARG		unsigned
#define CHAR_ARG		int
#define DOUBLE_ARG		double

#ifndef _WIN32
bool ismbblead (int c)
{
	return c > 128;
}

unsigned char * _mbsinc(const unsigned char * current)
{
	if (ismbblead(*(current++)))
	{
		if (*current != '\0')
		{
			current ++;
		}
	}
	return (unsigned char *)current;
}

size_t _mbclen (const unsigned char *c)
{
	return (ismbblead(*c) && c[1]!='\0') ? 2 : 1;
}
#endif // _WIN32

int __GetFormatLen(unsigned char * lpszFormat, va_list argList)
{
	va_list argListSave = argList;
	
	// make a guess at the maximum length of the resulting sstring
	int nMaxLen = 0;
	for (unsigned char * lpsz = lpszFormat; *lpsz != '\0'; lpsz = _mbsinc(lpsz))
	{
		// handle '%' character, but watch out for '%%'
		if (*lpsz != '%' || *(lpsz = _mbsinc(lpsz)) == '%')
		{
			nMaxLen += _mbclen(lpsz);
			continue;
		}
		
		int nItemLen = 0;
		
		// handle '%' character with format
		int nWidth = 0;
		for (; *lpsz != '\0'; lpsz = _mbsinc(lpsz))
		{
			// check for valid flags
			if (*lpsz == '#')
				nMaxLen += 2;   // for '0x'
			else if (*lpsz == '*')
				nWidth = va_arg(argList, int);
			else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
				*lpsz == ' ')
				;
			else // hit non-flag character
				break;
		}
		// get width and skip it
		if (nWidth == 0)
		{
			// width indicated by
			nWidth = atoi((char *)lpsz);
			for (; *lpsz != '\0' && isdigit(*lpsz); lpsz = _mbsinc(lpsz))
				;
		}
		
		ASSERT(nWidth >= 0);
		
		int nPrecision = 0;
		if (*lpsz == '.')
		{
			// skip past '.' separator (width.precision)
			lpsz = _mbsinc(lpsz);
			
			// get precision and skip it
			if (*lpsz == '*')
			{
				nPrecision = va_arg(argList, int);
				lpsz = _mbsinc(lpsz);
			}
			else
			{
				nPrecision = atoi((char *)lpsz);
				for (; *lpsz != '\0' && isdigit(*lpsz); lpsz = _mbsinc(lpsz))
					;
			}
			ASSERT(nPrecision >= 0);
		}
		
		// should be on type modifier or specifier
		int nModifier = 0;
		switch (*lpsz)
		{
			// modifiers that affect size
		case 'h':
			nModifier = FORCE_ANSI;
			lpsz = _mbsinc(lpsz);
			break;
		case 'l':
			nModifier = FORCE_UNICODE;
			lpsz = _mbsinc(lpsz);
			break;
			
			// modifiers that do not affect size
		case 'F':
		case 'N':
		case 'L':
			lpsz = _mbsinc(lpsz);
			break;
		}
		
		// now should be on specifier
		switch (*lpsz | nModifier)
		{
			// single characters
		case 'c':
		case 'C':
			nItemLen = 2;
			va_arg(argList, TCHAR_ARG);
			break;
		case 'c'|FORCE_ANSI:
		case 'C'|FORCE_ANSI:
			nItemLen = 2;
			va_arg(argList, CHAR_ARG);
			break;
		case 'c'|FORCE_UNICODE:
		case 'C'|FORCE_UNICODE:
			nItemLen = 2;
			va_arg(argList, WCHAR_ARG);
			break;
			
			// strings
		case 's':
			{
				unsigned char * pstrNextArg = va_arg(argList, unsigned char *);
				if (pstrNextArg == NULL)
					nItemLen = 6;  // "(null)"
				else
				{
					nItemLen = strlen((char *)pstrNextArg);
					nItemLen = __imax(1, nItemLen);
				}
				break;
			}
			
		case 'S':
			{
				char * pstrNextArg = va_arg(argList, char *);
				if (pstrNextArg == NULL)
					nItemLen = 6; // "(null)"
				else
				{
					nItemLen = strlen((char *)pstrNextArg);
					nItemLen = __imax(1, nItemLen);
				}
				break;
			}
			
		case 's'|FORCE_ANSI:
		case 'S'|FORCE_ANSI:
			{
				char * pstrNextArg = va_arg(argList, char *);
				if (pstrNextArg == NULL)
					nItemLen = 6; // "(null)"
				else
				{
					nItemLen = strlen(pstrNextArg);
					nItemLen = __imax(1, nItemLen);
				}
				break;
			}
			
		}
		
		// adjust nItemLen for strings
		if (nItemLen != 0)
		{
			nItemLen = __imax(nItemLen, nWidth);
			if (nPrecision != 0)
				nItemLen = __imin(nItemLen, nPrecision);
		}
		else
		{
			switch (*lpsz)
			{
				// integers
			case 'd':
			case 'i':
			case 'u':
			case 'x':
			case 'X':
			case 'o':
				va_arg(argList, int);
				nItemLen = 32;
				nItemLen = __imax(nItemLen, nWidth+nPrecision);
				break;
				
			case 'e':
			case 'f':
			case 'g':
			case 'G':
				va_arg(argList, DOUBLE_ARG);
				nItemLen = 128;
				nItemLen = __imax(nItemLen, nWidth+nPrecision);
				break;
				
			case 'p':
				va_arg(argList, void*);
				nItemLen = 32;
				nItemLen = __imax(nItemLen, nWidth+nPrecision);
				break;
				
				// no output
			case 'n':
				va_arg(argList, int*);
				break;
			default:
				ASSERT(0);  // unknown formatting option
			}
		}
		
		// adjust nMaxLen for output nItemLen
		nMaxLen += nItemLen;
}

return nMaxLen;
}

/**
 *	ʽַ?
 */
int StdString::printf(const char *lpszFormat, ...)
{
	va_list argList;
	va_start(argList, lpszFormat);
	int Len = __GetFormatLen((unsigned char *)lpszFormat, argList);
	if (Len) {
		resize (Len);
		Len = vsprintf(pbegin(), lpszFormat, argList);
		resize (Len);
	}
	va_end(argList);
	return Len;
}

static char *get_trim_chars()
{
	static char s_trim_chars[256] = {0};
	static bool first = true;
	if (first)
	{
		s_trim_chars[' '] = 1;
		s_trim_chars['\t'] = 1;
		s_trim_chars['\n'] = 1;
		s_trim_chars['\r'] = 1;
		first = false;
	}

	return s_trim_chars;
}

/**
 *	޼?ɾıߵĿոTABַ
 */
void StdString::trim_left ()
{
	int num = size ();
	if (num == 0)
		return;

	char *trim_chars = get_trim_chars();
	char *pbegin = (char *)c_str();
	const char *pend = pbegin + num;
	while (pbegin != pend) {
		char ch = *pbegin;
		//if (' ' == ch || '\t' == ch || '\n' == ch)
		if (trim_chars[ch] == 1)
			pbegin ++;
		else
			break;
	}
	if (pbegin != c_str())
		erase (0, pbegin - c_str());
}

/**
 *	޼?ɾıұߵĿոTABַ
 */
void StdString::trim_right ()
{
	int len = size();
	if (len == 0)
		return;

	char *trim_chars = get_trim_chars();
	const char *pbeg = pbegin();
	const char *pos = pend() - 1;
	while (pos != pbeg) {
		char ch = *pos;
		//if (' ' == ch || '\t' == ch || '\n' == ch)
		if (trim_chars[ch] == 1)
			pos --;
		else
			break;
	}
	if (pos != (pend() - 1))
	{
		erase (pos - pbeg + 1);
	}
}

/**
 *	?ַ?תΪdouble.
 *	@param ʹnposӿʼƥ䣬ûҵ���?
 *	@return طظֵ
 */
float64 StdString::to_double (int npos) const
{
	return atof (c_str() + npos);
}

/**
 *	ַ?תΪint.
 *	@param ʹnposӿʼƥ䣬ûҵ���?
 *	@return طظֵ
 */
int StdString::to_int (int npos) const
{
	return atoi (c_str() + npos);
}

#ifndef __STRICMP_DEFINED__
inline int stricmp(const char* p, const char* q)
{
    while (toupper(*(unsigned char*)p) == toupper(*(unsigned char*)q)) { 
        if (*p == '\0') { 
            return 0;
        }
        p += 1;
        q += 1;
    }
    return toupper(*(unsigned char*)p) - toupper(*(unsigned char*)q);
}
#endif

/**
 *	ִСдıȽ
 *	@param right 0Խβַ?
 *	@return ?رȽϽ
 *			- < 0	this < right
 *			- 0		this == right
 *			- > 0	this > right
 */
int StdString::compare_no_case (const char *right)
{
	return stricmp (c_str(), right);
	
}

/**
 *	?ִСдıȽ
 *	@param right ַ󴮶
 *	@return 󷵻رȽϽ
 *			- < 0	this < right
 *			- 0		this == right
 *			- > 0	this > right
 */
int StdString::compare_no_case (const StdString& right)
{
	return compare_no_case(right.c_str());
}

/**
 *	ַ?еĳضַ?滻�?
 *	@param oldstr	ԭַ?еضַ?
 *	@param newstr	滻ضַ󴮵�?
 */
void StdString::Replace(const char* oldstr, const char* newstr)
{
	int iOldLen = strlen(oldstr);
	int iNewLen = strlen(newstr);

	int iPos = 0;
	for( ; ; )
	{
		int iStart = find(oldstr, iPos);
		if(iStart == -1)
			break;

		replace(iStart, iOldLen, newstr);
		iPos = iStart + iNewLen;
	}
}

/**
 *	޼ıɾıĿոTABַ
 */
void StdString::trim_all()
{
	trim_left();
	trim_right();
}

/**
 *	@class StdFormat object.h "object.h"
 *	ַ󴮸ʽ.
 *	ʹñ
 *	<PRE>
 *	atoi (StdFormat ("%d", 223));
 *	</PRE>
 */
StdFormat::StdFormat(const char *lpszFormat, ...)
{
	if (NULL == lpszFormat)
		return;
	va_list argList;
	va_start(argList, lpszFormat);
	int Len = __GetFormatLen((unsigned char *)lpszFormat, argList);
	if (Len) {
		resize (Len);
		Len = vsprintf(pbegin(), lpszFormat, argList);
		resize (Len);
	}
	va_end(argList);
}

/**
 *	ӦóEventϢ.
 *	??ȫַEvAppEvent¼??ǿɸʽַ󴮣printͬ.
 */
void AppLog (const char* strFileName, const char *strFormat, ...)
{
	va_list argList;
	va_start(argList, strFormat);
	StdString strBuf;
	int Len = __GetFormatLen((unsigned char *)strFormat, argList);
	if (Len) {
		strBuf.resize(Len);
		try {
			Len = vsprintf(strBuf.pbegin(), strFormat, argList); 
		} catch (...) {
			strBuf = "AppLog : vsprintf error!";
		}

		ofstream file(strFileName, ios::app);
		if (file.good())
		{
			Time t (time(NULL));
			file << '[' << t.GetDisplayText().c_str() << "] " << strBuf.c_str() << "\n";
		}
	}
	va_end(argList);
}

HT_NAMESPACE_END
